package com.diao.myhub.schedule;

import com.diao.myhub.cache.AllTagCache;
import com.diao.myhub.cache.HotTagCache;
import com.diao.myhub.model.dto.HotTagDTO;
import com.diao.myhub.model.dto.TagDTO;
import com.diao.myhub.enums.TagTypeEnum;
import com.diao.myhub.model.bo.TagNode;
import com.diao.myhub.model.po.Question;
import com.diao.myhub.model.po.Tag;
import com.diao.myhub.service.QuestionService;
import com.diao.myhub.service.TagService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.LongAdder;

@Component
@Slf4j
@RequiredArgsConstructor
public class HotTagTasks {
    private final QuestionService questionService;
    private final HotTagCache hotTagCache;
    private final TagService tagService;
    private final AllTagCache allTagCache;

    @Scheduled(fixedRate = 1800000)
    public void hotTagSchedule() {
        log.info("hot tag flush task start {}", new Date());
        log.info("update tag cache from db");
        // 获取20年之内的所有问题
        long recentTime = 630_720_000_000L;
        long now = System.currentTimeMillis();
        List<Question> recentQuestions = new ArrayList<>();
        HashMap<String, Long[]> hotTagMap = new HashMap<>();
        int offset = 0;
        int limit = 20;
        while (offset == 0 || limit == recentQuestions.size()) {
            recentQuestions = questionService.getRecentQuestions(now - recentTime, offset, limit);
            for (Question recentQuestion : recentQuestions) {
                String[] tag = recentQuestion.getTag().trim().split(",");
                for (String t : tag) {
                    if (hotTagMap.get(t) != null) {
                        // 标签热度表不为空,更新该标签热度
                        // 热度计算:5*出现的标签数+标签所在问题的评论数
                        Long[] res = {
                                (hotTagMap.get(t)[0] + 5 + recentQuestion.getCommentCount()),
                                hotTagMap.get(t)[1] + 1,
                                hotTagMap.get(t)[2] + recentQuestion.getCommentCount()
                        };
                        hotTagMap.put(t, res);
                    } else {
                        Long[] res = {
                                (long) (5 + recentQuestion.getCommentCount()),
                                1L,
                                Long.valueOf(recentQuestion.getCommentCount())
                        };
                        hotTagMap.put(t, res);
                    }
                }
            }
            offset = offset + limit;
        }
        List<HotTagDTO> hotTagDTOList = new ArrayList<>();
        PriorityQueue<HotTagDTO> hotTagDTOS = hotTagCache.updateTags(hotTagMap);
        while (hotTagDTOS.peek() != null) {
            HotTagDTO poll = hotTagDTOS.poll();
            hotTagDTOList.add(poll);
        }
        Collections.reverse(hotTagDTOList);
        hotTagCache.setHotTags(hotTagDTOList);
        updateIndexesToDb(hotTagMap);
        allTagCache.updateCache();
        saveTagsToDdFromProperties(hotTagMap);
        log.info("hot tag flush task end {}", new Date());
        log.info("hot tag list {}", hotTagDTOList);
    }

    /**
     * 保存标签指数到数据库
     */
    private void updateIndexesToDb(HashMap<String, Long[]> hotTagMap){
        // 更新数据库
        ArrayList<Tag> updateList = new ArrayList<>(16);
        List<TagNode> tagNodes = tagService.listTagTree();
        tagNodes.forEach(tagNode -> {
            List<TagNode> childNodes = tagNode.getChildren();
            AtomicBoolean isChildUpdate = new AtomicBoolean(false);
            LongAdder trendAdder = new LongAdder();
            LongAdder qAdder = new LongAdder();
            LongAdder rAdder = new LongAdder();
            childNodes.forEach(childNode -> {
                if (hotTagMap.containsKey(childNode.getName())) {
                    childNode.setTrend(hotTagMap.get(childNode.getName())[0]);
                    childNode.setQCount(hotTagMap.get(childNode.getName())[1]);
                    childNode.setRCount(hotTagMap.get(childNode.getName())[2]);
                    Tag tag = new Tag();
                    BeanUtils.copyProperties(childNode, tag);
                    tag.setMTime(System.currentTimeMillis());
                    updateList.add(tag);
                    isChildUpdate.set(true);
                }
                trendAdder.add(childNode.getTrend());
                qAdder.add(childNode.getQCount());
                rAdder.add(childNode.getRCount());
            });
            if (isChildUpdate.get()) {
                tagNode.setRCount(rAdder.longValue());
                tagNode.setTrend(trendAdder.longValue());
                tagNode.setQCount(qAdder.longValue());
                tagNode.setMTime(System.currentTimeMillis());
                Tag tag = new Tag();
                BeanUtils.copyProperties(tagNode, tag);
                updateList.add(tag);
            }
        });
        tagService.updateBatchById(updateList);
    }
    /**
     * 从配置文件初始化标签
     *
     * @param hotTagMap
     */
    private void saveTagsToDdFromProperties(HashMap<String, Long[]> hotTagMap) {
        long count = tagService.count();
        if (count == 0) {
            log.info("start up init hotTag from properties!");
        } else {
            return;
        }
        List<TagDTO> tagCache = allTagCache.getTagCache();
        List<Tag> primaryTagList = new ArrayList<>();
        List<Tag> secondaryTagList = new ArrayList<>();
        LongAdder longAdder = new LongAdder();
        longAdder.add(tagCache.size());
        tagCache.forEach(tag -> {
            Tag modelTag = new Tag();
            modelTag.setId(tag.getCategoryId());
            modelTag.setPid(-1L);
            modelTag.setName(tag.getCategoryName());
            modelTag.setType(TagTypeEnum.CATEGORY.getTypeCode());
            modelTag.setDescription(tag.getCategoryName());
            modelTag.setMTime(System.currentTimeMillis());
            modelTag.setCTime(modelTag.getMTime());
            modelTag.setTrend(0L);
            modelTag.setRCount(0L);
            modelTag.setQCount(0L);
            primaryTagList.add(modelTag);
            List<String> tags = tag.getTags();
            tags.forEach(sTag -> {
                Tag sTagModel = new Tag();
                sTagModel.setType(TagTypeEnum.TAG.getTypeCode());
                sTagModel.setPid(modelTag.getId());
                sTagModel.setName(sTag);
                sTagModel.setMTime(System.currentTimeMillis());
                sTagModel.setCTime(sTagModel.getMTime());
                sTagModel.setDescription(sTag);
                longAdder.increment();
                sTagModel.setId(longAdder.longValue());
                if (hotTagMap.containsKey(sTag)) {
                    Long[] indexes = hotTagMap.get(sTag);
                    sTagModel.setTrend(indexes[0]);
                    sTagModel.setQCount(indexes[1]);
                    sTagModel.setRCount(indexes[2]);
                    // 修改父元素
                    modelTag.setTrend(sTagModel.getTrend() + modelTag.getTrend());
                    modelTag.setQCount(sTagModel.getQCount() + modelTag.getQCount());
                    modelTag.setRCount(sTagModel.getRCount() + modelTag.getRCount());
                } else {
                    sTagModel.setTrend(0L);
                    sTagModel.setRCount(0L);
                    sTagModel.setQCount(0L);
                }
                secondaryTagList.add(sTagModel);
            });
        });

        log.info("primaryTagList:{}", primaryTagList);
        log.info("secondaryTagList:{}", secondaryTagList);
        primaryTagList.addAll(secondaryTagList);
        tagService.saveBatch(primaryTagList);
    }

}
